package com.hhd.flow.engine.utils;

import com.hhd.flow.engine.vo.FlowModel;
import com.hhd.flow.engine.vo.FlowNode;
import com.hhd.flow.engine.vo.FlowNodeAndEdge;
import lombok.Getter;
import lombok.Setter;

import java.util.LinkedList;
import java.util.List;

@Getter
@Setter
public class FlowModelWalker {
    private FlowModel flowModel;
    private List<FlowNode> beginNodes;

    public FlowModelWalker(List<FlowNode> beginNodes, FlowModel flowModel) {
        this.beginNodes = beginNodes;
        this.flowModel = flowModel;
    }

    public void walk(WalkVisiter visiter) {
        for (FlowNode beginNode : beginNodes) {
            WalkContext context = WalkContext.begin(beginNode);
            walk(beginNode ,context, visiter);
        }
    }

    private void walk(FlowNode node, WalkContext context, WalkVisiter visiter) {
        List<FlowNodeAndEdge> nextNodeAndEdges = flowModel.getNextNodeAndEdgesById(node.getId());
        if (CommonUtils.isEmpty(nextNodeAndEdges)) return ;
        for (FlowNodeAndEdge nextNodeAndEdge : nextNodeAndEdges) {
            WalkContext childrenContext = context.addPath(nextNodeAndEdge);
            childrenContext.setCurrentNodeAndPath(nextNodeAndEdge);

            visiter.walk(childrenContext);
            walk(nextNodeAndEdge.getNode(), childrenContext, visiter);
        }
    }

    @Getter
    @Setter
    public static class WalkContext {
        FlowNode beginNode;
        List<FlowNodeAndEdge> path = CommonUtils.newList();
        FlowNodeAndEdge currentNodeAndPath;

        public static WalkContext begin(FlowNode beginNode) {
            WalkContext context = new WalkContext();
            context.setBeginNode(beginNode);
            return context;
        }

        public WalkContext addPath(FlowNodeAndEdge nextNodeAndEdge) {
            WalkContext context = new WalkContext();
            context.setBeginNode(getBeginNode());
            context.setPath(new LinkedList<>(context.getPath()));
            context.getPath().add(nextNodeAndEdge);
            return context;
        }

    }

    @FunctionalInterface
    public static interface WalkVisiter {
        public void walk(WalkContext context);
    }
}